﻿using SuperFrame.Charts.Axis;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SuperFrame.Charts
{
    [ContentProperty("Series")]
    public class Chart : Control
    {
        internal ContainerCollection containers;
        internal int indexCount = 0;
        //bool isMouseDragging;
        //bool isMouseClick;
        private bool isLegendUpdate;
        internal RootPanel rootDockPanel;
        internal Legend legend;
        internal List<SeriesColumn> SeriesColumn;
        internal ObservableCollection<LegendItem> legendItems;
        internal Border containerBorder;


        private static List<string> actualStringValues;
        internal static List<string> ActualStringValues
        {
            get { return actualStringValues; }
            set { actualStringValues = value; }
        }

        public Chart()
        {
            this.DefaultStyleKey = typeof(Chart);
            this.Series = new CollectionSeries();
            this.Series.CollectionChanged += OnCollectionSeriesChanged;
            brushes = Themes.MetroBrushes;
            legendItems = new ObservableCollection<LegendItem>();
            ActualStringValues = new List<string>();
            SeriesColumn = new List<SeriesColumn>();
            ResourceDictionary styles = new ResourceDictionary()
            {
                Source = new Uri(@"/SuperFrame.Charts;component/Themes/Styles.xaml", UriKind.Relative)
            };
            Application.Current.Resources.MergedDictionaries.Add(styles);
        }


        public override void OnApplyTemplate()
        {
            containers = (ContainerCollection)this.GetTemplateChild("PART_containers");
            if (containers != null)
                containers.Chart = this;
            rootDockPanel = (RootPanel)this.GetTemplateChild("Part_rootDockPanel");
            legend = (Legend)this.GetTemplateChild("Part_Legend");
            containerBorder = (Border)this.GetTemplateChild("PART_containerBorder");
            if (containerBorder != null)
                containerBorder.SetResourceReference(Border.StyleProperty, "ContainerBorderStyle");
            containers.ClipToBounds = true;
            BrushTheme();
            base.OnApplyTemplate();
            RefreshLegend();
        }
        public void RefreshLegend(bool IsRefreshed)
        {
            isLegendUpdate = IsRefreshed;
            RefreshLegend();
        }
        public void RefreshLegend()
        {
            if (!isLegendUpdate)
            {
                if (this.Legend != null)
                {
                    this.legendItems.Clear();
                    foreach (BaseSeries series in this.Series)
                    {
                        if (series is SeriesColumn)
                        {
                            foreach (var point in (series as SeriesColumn).Points)
                            {
                                LegendItem legendItem = new LegendItem();
                                legendItem.ShowIcon = true;
                                legendItem.Series = series;
                                legendItem.IconColor = point.Color;
                                legendItem.Label = point.Describe;// string.IsNullOrEmpty(point.Describe) ? point.YValue + "" : point.Describe;
                                this.legendItems.Add(legendItem);
                            }
                        }
                        else
                        {
                            LegendItem legendItem = new LegendItem();
                            legendItem.Series = series;
                            Binding showIconBinding = new Binding();
                            showIconBinding.Path = new PropertyPath("ShowIcon");
                            showIconBinding.Source = this.Legend;
                            BindingOperations.SetBinding(legendItem, LegendItem.ShowIconProperty, showIconBinding);
                            this.legendItems.Add(legendItem);
                        }
                    }
                    this.Legend.ItemsSource = this.legendItems;
                    isLegendUpdate = true;
                }

            }
        }

        void OnCollectionSeriesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:

                    foreach (BaseSeries series in e.NewItems)
                    {
                        Binding dataContextBinding = new Binding();
                        dataContextBinding.Path = new PropertyPath("DataContext");
                        dataContextBinding.Source = this;
                        BindingOperations.SetBinding(series, BaseSeries.DataContextProperty, dataContextBinding);
                        Binding renderingModeBinding = new Binding();
                        renderingModeBinding.Path = new PropertyPath("RenderingMode");
                        renderingModeBinding.Source = this;
                        BindingOperations.SetBinding(series, BaseSeries.RenderingModeProperty, renderingModeBinding);
                        series.Chart = this;
                        series.Index = indexCount;
                        series.XAxis = XAxis;
                        series.YAxis = YAxis;
                        indexCount++;
                        if (series is SeriesColumn)
                        {
                            SeriesColumn.Add(series as SeriesColumn);
                        }
                        isLegendUpdate = false;
                        RefreshLegend();
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    break;
                case NotifyCollectionChangedAction.Remove:
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Export the Sparrow Chart as Image
        /// </summary>
        /// <param name="fileName">Output File Name to Export the Chart as Image</param>
        public void ExportAsImage(string fileName)
        {
            string fileExtension = new FileInfo(fileName).Extension.ToLower(CultureInfo.InvariantCulture);

            BitmapEncoder imageEncoder = null;
            switch (fileExtension)
            {
                case ".bmp":
                    imageEncoder = new BmpBitmapEncoder();
                    break;

                case ".jpg":
                case ".jpeg":
                    imageEncoder = new JpegBitmapEncoder();
                    break;

                case ".png":
                    imageEncoder = new PngBitmapEncoder();
                    break;

                case ".gif":
                    imageEncoder = new GifBitmapEncoder();
                    break;

                case ".tif":
                case ".tiff":
                    imageEncoder = new TiffBitmapEncoder();
                    break;

                case ".wdp":
                    imageEncoder = new WmpBitmapEncoder();
                    break;

                default:
                    imageEncoder = new BmpBitmapEncoder();
                    break;
            }

            RenderTargetBitmap bmpSource = new RenderTargetBitmap((int)this.ActualWidth, (int)this.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            bmpSource.Render(this);

            imageEncoder.Frames.Add(BitmapFrame.Create(bmpSource));
            using (Stream stream = File.Create(fileName))
            {
                imageEncoder.Save(stream);
                stream.Close();
            }

        }

        /// <summary>
        /// Export the Sparrow Chart as Image
        /// </summary>
        /// <param name="fileName">Output File Name to Export the Chart as Image<</param>
        /// <param name="imageEncoder">Image Encoder Format for output image<</param>
        public void ExportAsImage(string fileName, BitmapEncoder imageEncoder)
        {
            string fileExtension = new FileInfo(fileName).Extension.ToLower(CultureInfo.InvariantCulture);

            RenderTargetBitmap bmpSource = new RenderTargetBitmap((int)this.ActualWidth, (int)this.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            bmpSource.Render(this);

            imageEncoder.Frames.Add(BitmapFrame.Create(bmpSource));
            using (Stream stream = File.Create(fileName))
            {
                imageEncoder.Save(stream);
                stream.Close();
            }
        }

        private void BrushTheme()
        {
            if (this.Series != null)
                foreach (var series in Series)
                {
                    if (series.Stroke == null)
                    {
                        if (brushes.Count > 1)
                            series.Stroke = brushes[series.Index % (brushes.Count - 1)];
                        else
                            series.Stroke = brushes[brushes.Count - 1];
                    }
                    if (series.isFill && (series as BaseFillSeries).Fill == null)
                    {
                        if (brushes.Count > 1)
                            (series as BaseFillSeries).Fill = brushes[series.Index % (brushes.Count - 1)];
                        else
                            (series as BaseFillSeries).Fill = brushes[brushes.Count - 1];
                    }
                }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            if (rootDockPanel != null)
            {
                rootDockPanel.Measure(constraint);
            }

            return base.MeasureOverride(constraint);
        }


        public XAxis XAxis
        {
            get { return (XAxis)GetValue(XAxisProperty); }
            set { SetValue(XAxisProperty, value); }
        }

        public static readonly DependencyProperty XAxisProperty = DependencyProperty.Register("XAxis", typeof(XAxis), typeof(Chart), new PropertyMetadata(null, OnXAxisChanged));
        private static void OnXAxisChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as Chart).XAxisChanged(args);
        }
        internal void XAxisChanged(DependencyPropertyChangedEventArgs args)
        {
            Binding seriesBinding = new Binding();
            seriesBinding.Path = new PropertyPath("Series");
            seriesBinding.Source = this;
            this.XAxis.SetBinding(AxisBase.SeriesProperty, seriesBinding);
            this.XAxis.Chart = this;
        }

        public YAxis YAxis
        {
            get { return (YAxis)GetValue(YAxisProperty); }
            set { SetValue(YAxisProperty, value); }
        }
        public static readonly DependencyProperty YAxisProperty = DependencyProperty.Register("YAxis", typeof(YAxis), typeof(Chart), new PropertyMetadata(null, OnYAxisChanged));
        private static void OnYAxisChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as Chart).YAxisChanged(args);
        }
        internal void YAxisChanged(DependencyPropertyChangedEventArgs args)
        {
            Binding seriesBinding = new Binding();
            seriesBinding.Path = new PropertyPath("Series");
            seriesBinding.Source = this;
            this.YAxis.SetBinding(AxisBase.SeriesProperty, seriesBinding);
            this.YAxis.Chart = this;
        }

        public Double AxisHeight
        {
            get { return (Double)GetValue(AxisHeightProperty); }
            set { SetValue(AxisHeightProperty, value); }
        }
        public static readonly DependencyProperty AxisHeightProperty = DependencyProperty.Register("AxisHeight", typeof(Double), typeof(Chart), new PropertyMetadata(30d));


        public Double AxisWidth
        {
            get { return (Double)GetValue(AxisWidthProperty); }
            set { SetValue(AxisWidthProperty, value); }
        }
        public static readonly DependencyProperty AxisWidthProperty = DependencyProperty.Register("AxisWidth", typeof(Double), typeof(Chart), new PropertyMetadata(30d));


        public bool IsRefresh
        {
            get { return (bool)GetValue(IsRefreshProperty); }
            set { SetValue(IsRefreshProperty, value); }
        }
        public static readonly DependencyProperty IsRefreshProperty = DependencyProperty.Register("IsRefresh", typeof(bool), typeof(Chart), new PropertyMetadata(true, OnIsRefreshChanged));
        private static void OnIsRefreshChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            Chart sender = obj as Chart;
            sender.IsRefreshChanged(args);
        }
        private void IsRefreshChanged(DependencyPropertyChangedEventArgs args)
        {
            foreach (BaseSeries series in Series)
            {
                series.IsRefresh = (bool)args.NewValue;
                if ((bool)args.NewValue)
                    series.Refresh();
            }

        }

        public CollectionSeries Series
        {
            get { return (CollectionSeries)GetValue(SeriesProperty); }
            set { SetValue(SeriesProperty, value); }
        }
        public static readonly DependencyProperty SeriesProperty = DependencyProperty.Register("Series", typeof(CollectionSeries), typeof(Chart), new PropertyMetadata(null));


        public Legend Legend
        {
            get { return (Legend)GetValue(LegendProperty); }
            set { SetValue(LegendProperty, value); }
        }
        public static readonly DependencyProperty LegendProperty = DependencyProperty.Register("Legend", typeof(Legend), typeof(Chart), new PropertyMetadata(null, OnLegendChanged));
        private static void OnLegendChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as Chart).LegendChanged(args);
        }
        internal void LegendChanged(DependencyPropertyChangedEventArgs args)
        {
            if (this.Legend != null)
            {
                this.Legend.Chart = this;
                this.Legend.DataContext = null;
            }
        }

        internal Dock LegendDock
        {
            get { return (Dock)GetValue(LegendDockProperty); }
            set { SetValue(LegendDockProperty, value); }
        }
        public static readonly DependencyProperty LegendDockProperty = DependencyProperty.Register("LegendDock", typeof(Dock), typeof(Chart), new PropertyMetadata(Dock.Top));


        public Theme Theme
        {
            get { return (Theme)GetValue(ThemeProperty); }
            set { SetValue(ThemeProperty, value); }
        }
        public static readonly DependencyProperty ThemeProperty = DependencyProperty.Register("Theme", typeof(Theme), typeof(Chart), new PropertyMetadata(Theme.Metro, OnThemeChanged));
        private static void OnThemeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as Chart).ThemeChanged(args);
        }
        internal void ThemeChanged(DependencyPropertyChangedEventArgs args)
        {
            switch (Theme)
            {
                case Theme.Metro:
                    brushes = Themes.MetroBrushes;
                    break;
                case Theme.Custom:
                    break;
                default:
                    break;
            }
            BrushTheme();
        }


        private List<Brush> brushes;
        public List<Brush> Brushes
        {
            get { return (List<Brush>)GetValue(BrushesProperty); }
            set { SetValue(BrushesProperty, value); }
        }
        public static readonly DependencyProperty BrushesProperty = DependencyProperty.Register("Brushes", typeof(List<Brush>), typeof(Chart), new PropertyMetadata(new List<Brush>()));

        public SmoothingMode SmoothingMode
        {
            get { return (SmoothingMode)GetValue(SmoothingModeProperty); }
            set { SetValue(SmoothingModeProperty, value); }
        }
        public static readonly DependencyProperty SmoothingModeProperty = DependencyProperty.Register("SmoothingMode", typeof(SmoothingMode), typeof(Chart), new PropertyMetadata(SmoothingMode.HighQuality));


        public CompositingQuality CompositingQuality
        {
            get { return (CompositingQuality)GetValue(CompositingQualityProperty); }
            set { SetValue(CompositingQualityProperty, value); }
        }
        public static readonly DependencyProperty CompositingQualityProperty = DependencyProperty.Register("CompositingQuality", typeof(CompositingQuality), typeof(Chart), new PropertyMetadata(CompositingQuality.HighQuality));

        public CompositingMode CompositingMode
        {
            get { return (CompositingMode)GetValue(CompositingModeProperty); }
            set { SetValue(CompositingModeProperty, value); }
        }
        public static readonly DependencyProperty CompositingModeProperty = DependencyProperty.Register("CompositingMode", typeof(CompositingMode), typeof(Chart), new PropertyMetadata(CompositingMode.SourceOver));


        public static double GetColumnMarginPercentage(DependencyObject obj)
        {
            return (double)obj.GetValue(ColumnMarginPercentageProperty);
        }
        public static void SetColumnMarginPercentage(DependencyObject obj, double value)
        {
            obj.SetValue(ColumnMarginPercentageProperty, value);
        }
        public static readonly DependencyProperty ColumnMarginPercentageProperty = DependencyProperty.RegisterAttached("ColumnMarginPercentage", typeof(double), typeof(Chart), new PropertyMetadata(0.3d));


        public RenderingMode RenderingMode
        {
            get { return (RenderingMode)GetValue(RenderingModeProperty); }
            set { SetValue(RenderingModeProperty, value); }
        }
        public static readonly DependencyProperty RenderingModeProperty = DependencyProperty.Register("RenderingMode", typeof(RenderingMode), typeof(Chart), new PropertyMetadata(RenderingMode.Default));


    }
}
